from abc import abstractmethod

import numpy

from classfile.class_reader import ClassReader, ConstantData
from utils.numer_utils import math
from utils.str_utils import decode_mutf8


class ConstantPool:

    def __init__(self, size):
        self.size = size
        self.constants = []

    def addConstant(self, ct) -> bool:
        self.constants.append(ct)
        if ct is not None:
            if ct.slotCount > 1:
                self.constants.append(None)
                return True
        return False

    def getConstant(self, index: int):
        return self.constants[index]

    def getConstantClass(self, index: int):
        return self.constants[index]


class Constant:

    def __init__(self):
        # 常量的tag标识
        self.tag: numpy.uint8 = numpy.uint8(0)
        # 常量占用的槽数  普通常量 占用1个 long/double 占用2个
        self.slotCount = 1
        # 字节数据
        self.data = None

    def setTag(self, tag):
        self.tag = tag

    @abstractmethod
    def parseValue(self, cp: ConstantPool):
        pass

    def readConstant(self, cr: ClassReader):
        pass

    @classmethod
    def new(cls):
        return cls()

    @abstractmethod
    def setData(self, cr: ClassReader):
        pass

    def fillData(self, cr: ClassReader, size: int):
        read_bytes = cr.readBytes(n=size)
        self.data = ConstantData(data=read_bytes, pos=0, capacity=size)


class ConstantInteger(Constant):
    def __init__(self):
        super().__init__()
        self.value: numpy.int32 = numpy.int32(0)

    def setData(self, cr: ClassReader):
        size = 4
        self.fillData(cr=cr, size=size)

    def parseValue(self, cp: ConstantPool):
        self.value = numpy.int32(self.data.readUInt32())


class ConstantFloat(Constant):
    def __init__(self):
        super().__init__()
        self.value: numpy.float32 = numpy.float32(0)

    def setData(self, cr: ClassReader):
        size = 4
        self.fillData(cr=cr, size=size)

    def parseValue(self, cp: ConstantPool):
        self.value = math.bit_to_float32(self.data.readUInt32())


class ConstantLong(Constant):
    def __init__(self):
        super().__init__()
        self.slotCount = 2
        self.value = None

    def setData(self, cr: ClassReader):
        size = 8
        self.fillData(cr=cr, size=size)

    def parseValue(self, cp: ConstantPool):

        self.value = numpy.int64(self.data.readUInt64())


class ConstantDouble(Constant):
    def __init__(self):
        super().__init__()
        self.slotCount = 2
        self.value = None

    def setData(self, cr: ClassReader):
        size = 8
        self.fillData(cr=cr, size=size)

    def parseValue(self, cp: ConstantPool):
        self.value = math.bit_to_float64(self.data.readUInt64())


class ConstantUTF8(Constant):
    def __init__(self):
        super().__init__()
        self.value: str = ''

    def setData(self, cr: ClassReader):
        size = cr.readUInt16()
        self.fillData(cr=cr, size=size)

    def parseValue(self, cp: ConstantPool):
        data = self.data.readBytes(n=self.data.capacity)
        print(data)
        self.value = decode_mutf8(data)


class ConstantString(Constant):
    def __init__(self):
        super().__init__()
        self.stringIndex: numpy.uint16 = numpy.uint16(0)
        self.value: ConstantUTF8 = None

    def setData(self, cr: ClassReader):
        size = 2
        self.fillData(cr=cr, size=size)
        self.stringIndex = self.data.readUInt16()

    def parseValue(self, cp: ConstantPool):
        self.value = cp.getConstant(index=int(self.stringIndex))


class ConstantClass(Constant):
    def __init__(self):
        super().__init__()
        self.nameIndex: numpy.uint16 = numpy.uint16(0)
        self.className: ConstantUTF8 = None

    def setData(self, cr: ClassReader):
        size = 2
        self.fillData(cr=cr, size=size)
        self.nameIndex = self.data.readUInt16()

    def parseValue(self, cp: ConstantPool):
        self.className = cp.getConstant(index=int(self.nameIndex))


class ConstantNameAndType(Constant):
    def __init__(self):
        super().__init__()
        self.nameIndex: numpy.uint16 = numpy.uint16(0)
        self.describeIndex: numpy.uint16 = numpy.uint16(0)
        self.name: ConstantUTF8 = None
        self.describe: ConstantUTF8 = None

    def setData(self, cr: ClassReader):
        size = 4
        self.fillData(cr=cr, size=size)
        self.nameIndex = self.data.readUInt16()
        self.describeIndex = self.data.readUInt16()

    def parseValue(self, cp: ConstantPool):
        self.name = cp.getConstant(index=int(self.nameIndex))
        self.describe = cp.getConstant(index=int(self.describeIndex))


class ConstantMemberRef(Constant):
    def __init__(self):
        super().__init__()
        self.classIndex: numpy.uint16 = numpy.uint16(0)
        self.nameAndTypeIndex: numpy.uint16 = numpy.uint16(0)
        self.klass: ConstantClass = None
        self.nameAndType: ConstantNameAndType = None

    def setData(self, cr: ClassReader):
        size = 4
        self.fillData(cr=cr, size=size)
        self.classIndex = self.data.readUInt16()
        self.nameAndTypeIndex = self.data.readUInt16()

    def getMethodName(self):
        return self.nameAndType.name.value

    def getDescriptor(self):
        return self.nameAndType.describe.value

    def parseValue(self, cp: ConstantPool):
        self.klass = cp.getConstant(index=int(self.classIndex))
        self.nameAndType = cp.getConstant(index=int(self.nameAndTypeIndex))


class ConstantFieldRef(ConstantMemberRef):
    def __init__(self):
        super().__init__()

    def getFieldName(self):
        return self.nameAndType.name.value

    def getFieldDescriptor(self):
        return self.nameAndType.describe.value


class ConstantMethodRef(ConstantMemberRef):
    def __init__(self):
        super().__init__()

    def getMethodName(self):
        return self.nameAndType.name.value

    def getDescriptor(self):
        return self.nameAndType.describe.value


class ConstantInterfaceMethodRef(ConstantMemberRef):
    def __init__(self):
        super().__init__()


class ConstantMethodType(Constant):
    def __init__(self):
        super().__init__()
        self.describeIndex: numpy.uint16 = numpy.uint16(0)
        self.describe: ConstantUTF8 = None

    def setData(self, cr: ClassReader):
        size = 2
        self.fillData(cr=cr, size=size)
        self.describeIndex = self.data.readUInt16()

    def parseValue(self, cp: ConstantPool):
        self.describe = cp.getConstant(index=int(self.describeIndex))


class ConstantMethodHandle(Constant):
    def __init__(self):
        super().__init__()
        self.referenceKind: numpy.uint8 = numpy.uint8(0)
        self.referenceIndex: numpy.uint16 = numpy.uint16(0)

    def setData(self, cr: ClassReader):
        size = 3
        self.fillData(cr=cr, size=size)
        self.referenceKind = self.data.readUInt8()
        self.referenceIndex = self.data.readUInt16()

    def parseValue(self, cp: ConstantPool):
        pass


class ConstantInvokeDynamic(Constant):
    def __init__(self):
        super().__init__()
        self.bootStrapMethodAttrIndex: numpy.uint16 = numpy.uint16(0)
        self.nameAndTypeIndex: numpy.uint16 = numpy.uint16(0)

    def setData(self, cr: ClassReader):
        size = 4
        self.fillData(cr=cr, size=size)
        self.bootStrapMethodAttrIndex = self.data.readUInt16()
        self.nameAndTypeIndex = self.data.readUInt16()

    def parseValue(self, cp: ConstantPool):
        pass


class ConstantHelper:
    CONSTANT_UTF_8 = 1
    CONSTANT_INTEGER = 3
    CONSTANT_FLOAT = 4
    CONSTANT_LONG = 5
    CONSTANT_DOUBLE = 6
    CONSTANT_CLASS = 7
    CONSTANT_STRING = 8
    CONSTANT_FIELD_REF = 9
    CONSTANT_METHOD_REF = 10
    CONSTANT_INTERFACE_METHOD_REF = 11
    CONSTANT_NAME_AND_TYPE = 12
    CONSTANT_METHOD_HANDLE = 15
    CONSTANT_METHOD_TYPE = 16
    CONSTANT_INVOKE_DYNAMIC = 18

    switch_dict = {
        CONSTANT_UTF_8: ConstantUTF8.new,
        CONSTANT_INTEGER: ConstantInteger.new,
        CONSTANT_FLOAT: ConstantFloat.new,
        CONSTANT_LONG: ConstantLong.new,
        CONSTANT_DOUBLE: ConstantDouble.new,
        CONSTANT_CLASS: ConstantClass.new,
        CONSTANT_STRING: ConstantString.new,
        CONSTANT_FIELD_REF: ConstantFieldRef.new,
        CONSTANT_METHOD_REF: ConstantMethodRef.new,
        CONSTANT_INTERFACE_METHOD_REF: ConstantInterfaceMethodRef.new,
        CONSTANT_NAME_AND_TYPE: ConstantNameAndType.new,
        CONSTANT_METHOD_HANDLE: ConstantMethodHandle.new,
        CONSTANT_METHOD_TYPE: ConstantMethodType.new,
        CONSTANT_INVOKE_DYNAMIC: ConstantInvokeDynamic.new
    }

    @staticmethod
    def newConstant(tag, cr: ClassReader):
        c = ConstantHelper.switch_dict[tag]()
        c.setTag(tag=tag)
        c.setData(cr=cr)
        return c
